Wesbos - 스크롤 → 슬라이드인


Untitled

진~~~짜 유용하게 잘 쓰일 것 같은 챕터다.
스크롤을 내리면 비어있던 이미지들이 위치에 맞게 슬라이드 인 되는 기능이다.

🤔 그동안은 클릭이나 키다운에 의한 이벤트 함수를 생성했었는데,
이번 챕터에서는 스크롤한 위치에 따른 기능실행이라는 점이 신선했던 것 같다.


로직

  1. const
  2. eventListener + 함수생성
  3. offset값을 활용한 위치값 불러오기

const!

1
const slideIn = document.querySelectorAll('.slide-in');

이벤트리스너와 함수

1
2
3
4
5
function slide(e) {
console.log(e);
}

window.addEventListener('scroll', slide);

콘솔창

하지만, 이렇게 둘 경우 너무 자주 함수(slide)가 실행되어 비효율적일 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//미리 입력되어 있던 debounce 함수 모듈을 활용해보자

function debounce(func, wait = 10, immediate = true) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

//slide함수에 적용될 수 있또록 감싸주자
window.addEventListener('scroll', debounce(slide));

offset 값을 활용!

💡 scrollY : 세로로 스크롤이 몇 px만큼 움직였는지 알 수 있음

💡 innerHeight : 현재 뷰포트의 세로값(px)을 알 수 있음

어느 위치를 기준으로 이미지가 슬라이드 인 되어야 할까?

슬라이드 위치

🤔 이미지의 세로 중간 쯤 뷰포트 바텀라인이 도착하면 슬라이드인 되도록 하는 것이 자연스러울 것 같다.

💡 그럼 ! 일단 현재 얼마나 스크롤 됐는지에 대한 값을 불러오는 변수를 설정해주자.


얼마나 스크롤 됐는지?

❗ 뷰포트 바닥을 기준으로 계산을 할 것이다. (상단 기준이면 scrollY만 있어도 됨)

1
2
3
// 스크롤한 만큼의 Y값(px) + 뷰포트의 Y길이(px)
const slideAt = window.scrollY + window.innerHeight;
console.log(slideAt);

이미지의 바닥 위치

1
2
// 떨어진 거리 + 이미지박스 높이 = 이미지 바닥의 위치
const imageBottom = slideImage.offsetTop + slideImage.height;

offsetTop?

💡 부모의 상단 보더로부터 자신의 상단 보더가 떨어진 만큼의 거리!

Untitled 3


슬라이드인 조건들

스크롤이 이미지의 절반까지 위치했는가?

1
const isHalfShown = slideAt > slideImage.offsetTop + slideImage.height / 2;

화면에서 이미지가 이미 지나쳐갔는가?

1
2
// 이미지가 스크롤되어 지나쳐, 이미 화면에서 안보이는데 굳이 띄워둘 필요는 없음!
const isPassed = window.scrollY < imageBottom;

이제 이미지를 위의 두 조건이 아닐 경우에는 이미지가 슬라이드 아웃 되도록 해주면 된다.


최종 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function debounce(func, wait = 10, immediate = true) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

const slideIn = document.querySelectorAll('.slide-in');

function slide(e) {
slideIn.forEach((slideImage) => {
const slideAt = window.scrollY + window.innerHeight;
console.log(slideAt);
console.dir(slideImage);
// bottom of the image
const imageBottom = slideImage.offsetTop + slideImage.height;
const isHalfShown = slideAt > slideImage.offsetTop + slideImage.height / 2;
const isPassed = window.scrollY < imageBottom;
if (isHalfShown && isPassed) {
slideImage.classList.add('active');
} else {
slideImage.classList.remove('active');
}
});
}

window.addEventListener('scroll', debounce(slide));

Author

Hoonjoo

Posted on

2022-01-04

Updated on

2022-02-07

Licensed under

Comments